﻿//-----------------------------------------------------------------------
// <copyright file="KnockoutExtensions.cs" company="deixei.com">
//     Copyright (c) deixei.com. All rights reserved.
// </copyright>
// <author>Marcio Parente</author>
//---------------------------------------------------------------------
namespace Deixei.Web
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using System.Web.Routing;
    using System.Web.Script.Serialization;
    using Deixei;
    
    using Deixei.Domain;

    /// <summary>
    /// Knockout Extensions : http://knockoutjs.com/
    /// </summary>
    public static class KnockoutExtensions
    {


        // Initialized the namespace
        //var DeixeiDomain = { };
        public static MvcHtmlString koInitializedNamespace(this HtmlHelper html, string namespaceName = "DeixeiDomain")
        {
            var sb = new StringBuilder();
            sb.AppendLine(string.Format("var {0} = {{ }};", namespaceName.Trim()));
            return MvcHtmlString.Create(sb.ToString());
        }

        public static MvcHtmlString koViewModel<TModel>(this HtmlHelper html, string namespaceName = "DeixeiDomain")
        {
            Type modelType = typeof(TModel);
            var sb = new StringBuilder();
            string entityName = modelType.Name;
            if(modelType.IsGenericType)
            {
                var x = from item in modelType.GenericTypeArguments
                        select item.Name;
                entityName = modelType.Name.Replace("`1", "") + "_" + string.Join("_", x.ToArray());
            }

            sb.AppendFormat(@"{1}.{0} = function(data) {{", entityName, namespaceName);

            sb.AppendLine("var self = this;");

            bool first = true;
            foreach ( var property in modelType.GetProperties() )
            {
                Type itemType = property.GetType();
                if ( first )
                {
                    first = false;
                }
                if ( typeof(IEnumerable).IsAssignableFrom(property.PropertyType) &&
                !typeof(string).IsAssignableFrom(property.PropertyType) &&
                property.PropertyType.IsGenericType )
                {
                    sb.AppendFormat(@"self.{0} = ko.observableArray([]);", property.Name);
                }
                else
                {
                    sb.AppendFormat(@"self.{0} = ko.observable(data.{0});", property.Name);
                    //sb.AppendFormat(@"self.{0} = ko.observable(data.{0} || """");", property.Name);
                }

            }
            sb.AppendLine("}");
            return MvcHtmlString.Create(sb.ToString());
        }

        public static MvcHtmlString koTextBoxFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
        {

            Type modelType = typeof(TModel);
            string modelName = modelType.Name;
            string labelName = ExpressionHelper.GetExpressionText(expression);
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

            var sb = new StringBuilder();

            string longDescription = metadata.Description;
            string displayName = metadata.DisplayName ?? metadata.ShortDisplayName ?? metadata.PropertyName;
            string propertyName = metadata.PropertyName;

            Dictionary<string, object> htmlAttributes = new Dictionary<string, object>();
            htmlAttributes.Add("placeholder", displayName);
            htmlAttributes.Add("data-bind", "value: " + propertyName);

            sb.AppendLine(htmlHelper.LabelFor(expression).ToHtmlString());
            sb.AppendLine(htmlHelper.TextBoxFor(expression, htmlAttributes).ToHtmlString());
            sb.AppendLine(htmlHelper.ValidationMessageFor(expression).ToHtmlString());
            sb.AppendFormat(@"<span class=""help-block"">{0}</span>", longDescription);
            return MvcHtmlString.Create(sb.ToString());
        }

        public static MvcHtmlString koInput<TModel>(this HtmlHelper<TModel> html, string labelName, ViewContext viewContext, IViewDataContainer viewDataContainer)
        {
            ControllerContext context = viewContext.Controller.ControllerContext;

            Type modelType = typeof(TModel);
            string modelName = modelType.Name;
            ModelMetadataProvider mmp = ModelMetadataProviders.Current;
            ModelMetadata metadata = mmp.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), labelName);

            var sb = new StringBuilder();

            string longDescription = metadata.Description;
            string displayName = metadata.DisplayName ?? metadata.ShortDisplayName ?? metadata.PropertyName;
            string name = metadata.PropertyName;
            string required = metadata.IsRequired ? "required" : "";

            sb.AppendFormat(@"<label>{0}</label>", displayName);
            sb.AppendFormat(@"<input type=""text"" ");
            sb.AppendFormat(@"id=""{0}_{1}"" ", modelName, name);
            sb.AppendFormat(@"name=""{0}.{1}"" ", modelName, name);
            sb.AppendFormat(@"placeholder=""{1}"" data-bind=""value: {0}"" ", name, displayName);

            var f = ApplyFieldValidationMetadata(html, metadata, labelName);

            if ( metadata.IsRequired )
            { 
                sb.AppendFormat(@"class=""required"" ");
                sb.AppendFormat(@"data-val-required=""The field is required.""  ");
                sb.AppendFormat(@"data-val=""true"" ");
            }

            sb.AppendFormat(@"/>");
            sb.AppendFormat(@"<span class=""help-block"">{0}</span>", longDescription);

            HtmlHelper<NavigationArea> d = new HtmlHelper<NavigationArea>(viewContext, viewDataContainer);
            
            var l = d.LabelFor(r => r.Area).ToHtmlString();
            var a = d.TextBoxFor(r => r.Area).ToHtmlString();
            var x = d.ValidationMessageFor(r => r.Area).ToHtmlString();

            sb.AppendLine(l);
            sb.AppendLine(a);
            sb.AppendLine(x);

            return MvcHtmlString.Create(sb.ToString());
        }

        //public static void koValidateFor<TModel>(this HtmlHelper htmlHelper, string modelName )
        //{

        //    Type modelType = typeof(TModel);
        //    ModelMetadataProvider mmp = ModelMetadataProviders.Current;
        //    ModelMetadata modelMetadata = mmp.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), modelName);

        //    ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
        //}
        private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName)
        {
            FormContext formContext = htmlHelper.ViewContext.FormContext;
            FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */);

            // write rules to context object
            IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);
            foreach ( ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules()) )
            {
                fieldMetadata.ValidationRules.Add(rule);
            }

            return fieldMetadata;
        }

        //public static MvcHtmlString koValidationMessageFor<TModel>(this HtmlHelper htmlHelper, string modelName, string validationMessage = null, string cssClass = null, string dir = null, string id = null, string lang = null, string style = null, string title = null)
        //{

        //    Type modelType = typeof(TModel);
        //    ModelMetadataProvider mmp = ModelMetadataProviders.Current;
        //    ModelMetadata modelMetadata = mmp.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), modelName);

        //    if ( string.IsNullOrEmpty(validationMessage) )
        //    {
        //        validationMessage = "????????";
        //    }


        //    return htmlHelper.ValidationMessageHelper(modelMetadata, modelName, "DEmo", SpanAttributes(cssClass, dir, id, lang, style, title));
        //}

        //private static MvcHtmlString ValidationMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes)
        //{
        //    string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
        //    FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();

        //    if ( !htmlHelper.ViewData.ModelState.ContainsKey(modelName) && formContext == null )
        //    {
        //        return null;
        //    }

        //    ModelState modelState = htmlHelper.ViewData.ModelState[modelName];
        //    ModelErrorCollection modelErrors = ( modelState == null ) ? null : modelState.Errors;
        //    ModelError modelError = ( ( ( modelErrors == null ) || ( modelErrors.Count == 0 ) ) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0] );

        //    if ( modelError == null && formContext == null )
        //    {
        //        return null;
        //    }

        //    TagBuilder builder = new TagBuilder("span");
        //    builder.MergeAttributes(htmlAttributes);
        //    builder.AddCssClass(( modelError != null ) ? HtmlHelper.ValidationMessageCssClassName : HtmlHelper.ValidationMessageValidCssClassName);

        //    if ( !String.IsNullOrEmpty(validationMessage) )
        //    {
        //        builder.SetInnerText(validationMessage);
        //    }
        //    else if ( modelError != null )
        //    {
        //        builder.SetInnerText(GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState));
        //    }

        //    if ( formContext != null )
        //    {
        //        bool replaceValidationMessageContents = String.IsNullOrEmpty(validationMessage);

        //        if ( htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled )
        //        {
        //            builder.MergeAttribute("data-valmsg-for", modelName);
        //            builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant());
        //        }
        //        else
        //        {
        //            FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
        //            // rules will already have been written to the metadata object
        //            fieldMetadata.ReplaceValidationMessageContents = replaceValidationMessageContents; // only replace contents if no explicit message was specified

        //            // client validation always requires an ID
        //            builder.GenerateId(modelName + "_validationMessage");
        //            fieldMetadata.ValidationMessageId = builder.Attributes["id"];
        //        }
        //    }

        //    return builder.ToMvcHtmlString(TagRenderMode.Normal);
        //}

        //private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
        //{
        //    if ( !String.IsNullOrEmpty(error.ErrorMessage) )
        //    {
        //        return error.ErrorMessage;
        //    }
        //    if ( modelState == null )
        //    {
        //        return null;
        //    }

        //    string attemptedValue = ( modelState.Value != null ) ? modelState.Value.AttemptedValue : null;
        //    return String.Format(CultureInfo.CurrentCulture, "", attemptedValue);
        //}

        //private static IDictionary<string, object> SpanAttributes(string cssClass, string dir, string id, string lang, string style, string title)
        //{
        //    var htmlAttributes = Attributes(cssClass, id, style);

        //    htmlAttributes.AddOptional("dir", dir);
        //    htmlAttributes.AddOptional("lang", lang);
        //    htmlAttributes.AddOptional("title", title);

        //    return htmlAttributes;
        //}

        //private static IDictionary<string, object> Attributes(string cssClass, string id, string style)
        //{
        //    var htmlAttributes = new RouteValueDictionary();

        //    htmlAttributes.AddOptional("class", cssClass);
        //    htmlAttributes.AddOptional("id", id);
        //    htmlAttributes.AddOptional("style", style);

        //    return htmlAttributes;
        //}

        //private static void AddOptional(this IDictionary<string, object> dictionary, string key, object value)
        //{
        //    if ( value != null )
        //    {
        //        dictionary[key] = value;
        //    }
        //}

    }
}
